<?php
class DB_query 
{
  
  /* public: connection parameters */
  
  //--------------database config start----------------------------  
  //单个正在操作的数据库配置
  var $Host     = "";
  var $Port="";  //tom add 20080310
  var $Database = "";
  
  var $User     = "";
  var $Password = "";

  var $Site_Num="";//分布节点编号
  var $TimeOut="";//检测数据库服务器及数据库端口是否可用的超时时间，避免程序无限制等待
  
  //-----日志参数------------
  var $If_Log="0"; //是否记录日志
  var $If_Log_All="0";//是否记录所有SQL,否就代表只记录update,delete,call 
  var $Log_Url="/log";//日志路经
  var $Log_Cookie_User="pfadmin_admin_id"; //记录操作员的ID;
  //------------------------------
  
 //MASTER数据库集群,用来处理非查询的SQL语句 
  
  var $Master_List;
    //SLAVE数据库集群，用来处理查询语句
  var $Slave_List;
  var $Slave_Check_All;//如果本机SLAVE异常，是否要轮循去查其他SLAVE找出可用的SLAVE已提供服务
     //-------------config end-------------------------------------


  /* public: configuration parameters */
  var $Auto_Free     = 1;     ## Set to 1 for automatic mysql_free_result()
  var $Debug         = 0;     ## Set to 1 for debugging messages.
  var $Halt_On_Error = "yes"; //report  ## "yes" (halt with message,if have error then session die), "no" (ignore errors quietly，skip die), "report" (ignore errror, but spit a warning and skip die)
  var $Seq_Table     = "db_sequence";

  /* public: result array and current row number */
  var $Record   = array();
  var $Row;

  /* public: current error number and error text */
  var $Errno    = 0;
  var $Error    = "";

  /* public: this is an api revision, not a CVS revision. */
  var $type     = "mysql";
  var $revision = "1.2";

  /* private: link and query handles */
  var $Link_ID  = 0;
  var $Query_ID = 0;



  /* public: constructor */
  function db_mysql($query = "") {
      $this->query($query);
  }

  /* public: some trivial reporting */
  function link_id() {
    return $this->Link_ID;
  }

  function query_id() {
    return $this->Query_ID;
  }
  
  
  function test_connect($test_type,$site_num)
  {
  //	echo $this->TimeOut;
  	$test_type=trim(strtoupper($test_type));
   
    $return_value=0;   
    //如果是检测非查询SQL要用的数据库服务
    if($test_type!="SELECT")
    {
       foreach($this->Master_List as $k=>$v)
       {
         if($v["Site_Num"]==$site_num)
         {
           if($this->Halt_On_Error=="no")
       $fp = @fsockopen($v["Host"],$v["Port"], &$errno, &$errstr, $this->TimeOut);
      else
          $fp = fsockopen($v["Host"],$v["Port"], &$errno, &$errstr, $this->TimeOut);
      if($fp)
      {
      	fclose($fp); 
      	$return_value=1;
      }
      else 
         $return_value =0;
       
           break;
         }	
       }
    }
    
    //如果是查询SQL
    else
    { 
       foreach($this->Slave_List as $k=>$v)
       {
         if($v["Site_Num"]==$site_num)
         {
              if($this->Halt_On_Error=="no")
       $fp = @fsockopen($v["Host"],$v["Port"], &$errno, &$errstr, $this->TimeOut);
      else
          $fp = fsockopen($v["Host"],$v["Port"], &$errno, &$errstr, $this->TimeOut);
      if($fp)
      {
      	fclose($fp); 
      	$return_value=1;
      }
      else 
         $return_value=0;
         
          break;
         }	
       }
    }
    
//--------过滤SQL类型结束----------------------------------
   
    //tom add start 20080227
  //	$tmp=$this->connect();
  	//如果没有连接上,则进行轮询
  	
  	if($return_value==0)
  	{
  	  //如果要轮循
  	  if($this->Slave_Check_All==1)
  	  {
  	  	$x=0;
  	  	foreach($this->Slave_List as $k=>$v)
        {
          if($v["Site_Num"]!=$this->Site_Num)
          {
             
         if($this->Halt_On_Error=="no")
       $fp = @fsockopen($v["Host"],$v["Port"], &$errno, &$errstr, $this->TimeOut);
      else
          $fp = fsockopen($v["Host"],$v["Port"], &$errno, &$errstr, $this->TimeOut);
      if($fp)
      {
      	fclose($fp); 
      	$return_value=1;
      	$x=1;
      	break;
      }
           
          }	
        }
        
       
  	  }
  	  	  	
  	}
  	
   return $return_value;
  	
  } 

  /* public: connection management */
  function connect($Database = "", $Host = "", $User = "", $Password = "") 
  {
 //   echo "connected";
 //   echo "now->".$this->Host;
    /* Handle defaults */
    if ("" == $Database)
      $Database = $this->Database;
    if ("" == $Port)
      $Port = $this->Port;
      
    if ("" == $Host)
      $Host     = $this->Host;
    if ("" == $User)
      $User     = $this->User;
    if ("" == $Password)
      $Password = $this->Password;

//  include $_SERVER['DOCUMENT_ROOT']."/includes/Crypt.php";	
  // 	$sc = new SysCrypt();
  // 	echo $User;
   //	$User = $sc -> php_decrypt($User);
   //	$Password = $sc -> php_decrypt($Password);
  	//echo $User."-".$Password;
  	
// echo "xxx".$this->Host." ".$User." ".$Password;
     
   // echo "<BR>Current_Link_ID:".$this->Link_ID;
    /* establish connection, select database */
    if ( 0 == $this->Link_ID ) 
    {
    	// $this->Link_ID=@mysql_connect($Host, $User, $Password);
      
      //如果系统是要求屏蔽错误提示，肯定是EC系统为了不让用户看到错误提示，因为，EC系统已经将用户本应该写入到数据库的内容写入到了LOG文件，
      //这时候用户的购物行为应该算做OK,所以不应该再让让系统提示出数据库访问的异常，而只需要程序内部知道有异常即可。
      
   //  echo $this->TimeOut;
   /*   if($this->Halt_On_Error=="no")
       $fp = @fsockopen($Host,$Port, &$errno, &$errstr, $this->TimeOut);
      else
         $fp = fsockopen($Host,$Port, &$errno, &$errstr, $this->TimeOut);
      if($fp)
      {
     	   
     	fclose($fp); 
    */ 

      if($this->Halt_On_Error=="no")
        $this->Link_ID=@mysql_connect($Host.":".$Port, $User, $Password);
      else
        $this->Link_ID=mysql_connect($Host.":".$Port, $User, $Password);
     
      //如果是mysql server服务无法链接
      if (!$this->Link_ID) 
      { 
        $this->halt("connect($Host.':'.$Port, $User, \$Password) failed.");
       
        return 0;
     
      }
     
    /* }
     else
     { $this->Link_ID=0;
       return 0;	
     } 
     */
      //如果库无法链接
      if (!@mysql_select_db($Database,$this->Link_ID)) 
      {
        $this->halt("cannot use database ".$this->Database);
        return 0;
      }
    }
    return $this->Link_ID;
 }

  /* public: discard the query result */
  function free() 
  {
      @mysql_free_result($this->Query_ID);
      $this->Query_ID = 0;
  }

  /* public: perform a query */
  function query($Query_String,$returnType='') 
  {
 // echo "1";
    /* No empty queries, please, since PHP4 chokes on them. */
     if($Query_String == "")
      /* The empty query string is passed on from the constructor,
       * when calling the class without a query, e.g. in situations
       * like these: '$db = new db_mysql_Subclass;'
       */
      return 0;
//echo $Query_String."<br>";
//--------过滤SQL类型开始，是查询SQL还是执行SQL start------
    $temp_sql=trim(strtoupper($Query_String));
  //  $temp_pos=strpos($temp_sql,"SELECT");
    $temp_pos=substr($temp_sql,0,6);
//echo "xxx=".$temp_pos;    
     $x_temp_pos=strpos($temp_sql,"@");
    
    //如果时轮询，则强制Halt_on_error,已确保异常的时候session不会die掉
    if($this->Slave_Check_All==1)
  	{
  		$this->Halt_On_Error="no";
  	}	
   //如果是非查询SQL
   // if($temp_pos===false)
    if($temp_pos!="SELECT"||(!($x_temp_pos===false)))  //如果是执行SQL或者是执行存储过程或者是触发器的后续结果
    {
       foreach($this->Master_List as $k=>$v)
       {
         if($v["Site_Num"]==$this->Site_Num)
         {
           //如果不是链接同一台服务器
           if($this->Host!=$v["Host"]||$this->Port!=$v["Port"])
           {
             //如果当前没有链接
             if( 0 == $this->Link_ID ) 
             {
             	
             }
             //如果有链接,则要断开链接，因为现在要访问的是其他服务器，而CONNECT函数里面如果链接存在则不再建立新的链接，所以切换的目的会失败
             else
             {
             	
            // 	  echo "<br>close current connection";
             	  $this->Link_ID=0;
            // 	  mysql_close(); //关闭已有的链接
             }
           }
           $this->Host=$v["Host"];
           $this->Port=$v["Port"];
           $this->Database=$v["Database"];
           $this->User=$v["User"];
           $this->Password=$v["Password"];
           
  	  //   echo "<br>debug>> deal sql,link to mater database server:".$this->Host.":".$this->Port."/".$this->Database."/".$this->User."/".$this->Password;
           break;
        //   die;
         }	
       }
    }
    
    //如果是查询SQL
    else
    { 
       foreach($this->Slave_List as $k=>$v)
       {
         if($v["Site_Num"]==$this->Site_Num)
         {
           //如果不是链接同一台服务器
           if($this->Host!=$v["Host"]||$this->Port!=$v["Port"])
           {
             //如果当前没有链接
             if( 0 == $this->Link_ID ) 
             {
             }
             //如果有链接,则要断开链接，因为现在要访问的是其他服务器，而CONNECT函数里面如果链接存在则不再建立新的链接，所以切换的目的会失败
             else
             {
          //   	  echo "<br>close current connection";
             	  $this->Link_ID=0;
             //	  mysql_close(); //关闭已有的链接
             }
           }
           $this->Host=$v["Host"];
            $this->Port=$v["Port"];
           $this->Database=$v["Database"];
           $this->User=$v["User"];
           $this->Password=$v["Password"];
  	       
  	 //   echo "<br>debug>> query sql,link to slave database server:".$this->Host.":".$this->Port."/".$this->Database."/".$this->User."/".$this->Password;
          break;
         }	
       }
    }
    
//--------过滤SQL类型结束----------------------------------
  	//如果没有连接上
  	
  	if(!$this->connect())
  	{
  	 if($temp_pos!="SELECT"||(!($x_temp_pos===false)))  //如果是执行SQL或者是执行存储过程或者是触发器的后续结果
     {
     	return 0;
     }
  	 else
  	 {
  	 	  //如果要轮循
  	  if($this->Slave_Check_All==1)
  	  {
  	  	
  	  	$x=0;
  	  	foreach($this->Slave_List as $k=>$v)
        {
          if($v["Site_Num"]!=$this->Site_Num)
          {
           
           if($this->Host!=$v["Host"]||$this->Port!=$v["Port"])
           {
             //如果当前没有链接
             if( 0 == $this->Link_ID ) 
             {
             }
             //如果有链接,则要断开链接，因为现在要访问的是其他服务器，而CONNECT函数里面如果链接存在则不再建立新的链接，所以切换的目的会失败
             else
             {
          //   	  echo "<br>close current connection";
             	  $this->Link_ID=0;
            // 	  mysql_close(); //关闭已有的链接
             }
           }
           $this->Host=$v["Host"];
            $this->Port=$v["Port"];
           $this->Database=$v["Database"];
           $this->User=$v["User"];
           $this->Password=$v["Password"];
  	//     echo "<br>debug>> query sql,link to slave database server:".$this->Host."/".$this->Database."/".$this->User."/".$this->Password;
            //如果不是链接同一台服务器
           
           if(!$this->connect())
   	       { 
   	       }
   	       else 
   	       {
   	       	 //链接上
   	       	  $x=1; 
   	       	  break;
   	       }
          }	
        }
        
        if($x==0)
        {
      //  	echo "deal-fial:0";
        	return 0;
        }
    
  	  }
  	  //如果不要轮询
  	  else
  	  {
  	  //	echo "deal-fial:0";
  	  	return 0;
  	  }	 
  	  
  	 } 
  	  
  	   	
  	}
  
  
  	
    //如果链接MYSQL数据库服务器或者是数据库有异常，则返回0
    //tom cancel start 20080227
   //非查询SQL
  /*  if($temp_pos===false)
     $xx=$this->test_connect("UPDATE",$this->Site_Num);
   //查询SQL
    else
     $xx=$this->test_connect("SELECT",$this->Site_Num);
   
   if($xx!=0)
   {
      if(!$this->connect()) 
      {
        return 0; 
      };
   }
   else
   {
      return 0; 	
   }
    if(!$this->connect()) 
    {
      return 0; 
    };
   */
   //tom cancel end 20080227
   
   /* we already complained in connect() about that. */
   
    # New query, discard previous result.
    if($this->Query_ID) 
    {
      $this->free();
    }

    if($this->If_Log=="1")
    {
       if($this->If_Log_All=="1"||($temp_pos!="SELECT"||(!($x_temp_pos===false))) )
    	
    	$this->log_result($Query_String,$this->Log_Url);
    }
    if($this->Debug)
      printf("Debug: query = %s<br>\n", $Query_String);
	  mysql_query("set names 'utf8';"); // tom add
   
$this->Query_ID = @mysql_query($Query_String,$this->Link_ID);
    $this->Row   = 0;
    $this->Errno = mysql_errno();
    $this->Error = mysql_error();
    if (!$this->Query_ID) 
    {
      $this->halt("Invalid SQL: ".$Query_String);
    }

   
    # Will return nada if it fails. That's fine.
	//Edit by eagle 2007-03-06 19:22
	if ($returnType == '') {
		return $this->Query_ID;
	} 
	elseif ($returnType == '1') 
	{
		$row = $this->fetchRow($this->Query_ID);
		return $row["0"];
	}    
 
 }

  /* public: walk result set */
  function next_record() 
  {
    if (!$this->Query_ID) {
      $this->halt("next_record called with no query pending.");
      return 0;
    }

    $this->Record = @mysql_fetch_array($this->Query_ID);
    $this->Row   += 1;
    $this->Errno  = mysql_errno();
    $this->Error  = mysql_error();

    $stat = is_array($this->Record);
    if (!$stat && $this->Auto_Free) {
      $this->free();
    }
    return $stat;
  }
  function freeResult($res)
  {
  return mysql_free_result($res);
  }
  function fetchRow($res)
  {
  return mysql_fetch_row($res);
  }
  function fetchArray($res)
  {
  	return mysql_fetch_array($res , MYSQL_ASSOC);
  }

  /* public: position in result set */
  function seek($pos = 0) {
    $status = @mysql_data_seek($this->Query_ID, $pos);
    if ($status)
      $this->Row = $pos;
    else {
      $this->halt("seek($pos) failed: result has ".$this->num_rows()." rows");

      /* half assed attempt to save the day,
       * but do not consider this documented or even
       * desireable behaviour.
       */
      @mysql_data_seek($this->Query_ID, $this->num_rows());
      $this->Row = $this->num_rows;
      return 0;
    }

    return 1;
  }

  /* public: table locking */
  function lock($table, $mode="write") {
    $this->connect();

    $query="lock tables ";
    if (is_array($table)) {
      while (list($key,$value)=each($table)) {
        if ($key=="read" && $key!=0) {
          $query.="$value read, ";
        } else {
          $query.="$value $mode, ";
        }
      }
      $query=substr($query,0,-2);
    } else {
      $query.="$table $mode";
    }
	mysql_query("set names 'utf8';"); // tom add
   
    $res = @mysql_query($query, $this->Link_ID);
    if (!$res) {
      $this->halt("lock($table, $mode) failed.");
      return 0;
    }
    return $res;
  }

  function unlock() {
    $this->connect();

    $res = @mysql_query("unlock tables");
    if (!$res) {
      $this->halt("unlock() failed.");
      return 0;
    }
    return $res;
  }


  /* public: evaluate the result (size, width) */
  function affected_rows() {
    return @mysql_affected_rows($this->Link_ID);
  }

  function num_rows() {
    return @mysql_num_rows($this->Query_ID);
  }

  function insert_id() {
    return @mysql_insert_id($this->Link_ID);
  }

  function num_fields() {
    return @mysql_num_fields($this->Query_ID);
  }

  /* public: shorthand notation */
  function nf() {
    return $this->num_rows();
  }

  function np() {
    print $this->num_rows();
  }

  function f($Name) {
    return $this->Record[$Name];
  }

  function p($Name) {
    print $this->Record[$Name];
  }

  /* public: sequence numbers */
  function nextid($seq_name) {
    $this->connect();

    if ($this->lock($this->Seq_Table)) {
      /* get sequence number (locked) and increment */
      $q  = sprintf("select nextid from %s where seq_name = '%s'",
                $this->Seq_Table,
                $seq_name);
	  mysql_query("set names 'utf8';");// tom add
   
      $id  = @mysql_query($q, $this->Link_ID);
      $res = @mysql_fetch_array($id);

      /* No current value, make one */
      if (!is_array($res)) {
        $currentid = 0;
        $q = sprintf("insert into %s values('%s', %s)",
                 $this->Seq_Table,
                 $seq_name,
                 $currentid);
		mysql_query("set names 'utf8';");//tom add
   
        $id = @mysql_query($q, $this->Link_ID);
      } else {
        $currentid = $res["nextid"];
      }
      $nextid = $currentid + 1;
      $q = sprintf("update %s set nextid = '%s' where seq_name = '%s'",
               $this->Seq_Table,
               $nextid,
               $seq_name);
	  mysql_query("set names 'utf8';");// tom add
   
      $id = @mysql_query($q, $this->Link_ID);
      $this->unlock();
    } else {
      $this->halt("cannot lock ".$this->Seq_Table." - has it been created?");
      return 0;
    }
    return $nextid;
  }

  /* public: return table metadata */
  function metadata($table='',$full=false) {
    $count = 0;
    $id    = 0;
    $res   = array();

    /*
     * Due to compatibility problems with Table we changed the behavior
     * of metadata();
     * depending on $full, metadata returns the following values:
     *
     * - full is false (default):
     * $result[]:
     *   [0]["table"]  table name
     *   [0]["name"]   field name
     *   [0]["type"]   field type
     *   [0]["len"]    field length
     *   [0]["flags"]  field flags
     *
     * - full is true
     * $result[]:
     *   ["num_fields"] number of metadata records
     *   [0]["table"]  table name
     *   [0]["name"]   field name
     *   [0]["type"]   field type
     *   [0]["len"]    field length
     *   [0]["flags"]  field flags
     *   ["meta"][field name]  index of field named "field name"
     *   The last one is used, if you have a field name, but no index.
     *   Test:  if (isset($result['meta']['myfield'])) { ...
     */

    // if no $table specified, assume that we are working with a query
    // result
    if ($table) {
      $this->connect();
      $id = @mysql_list_fields($this->Database, $table);
      if (!$id)
        $this->halt("Metadata query failed.");
    } else {
      $id = $this->Query_ID;
      if (!$id)
        $this->halt("No query specified.");
    }

    $count = @mysql_num_fields($id);

    // made this IF due to performance (one if is faster than $count if's)
    if (!$full) {
      for ($i=0; $i<$count; $i++) {
        $res[$i]["table"] = @mysql_field_table ($id, $i);
        $res[$i]["name"]  = @mysql_field_name  ($id, $i);
        $res[$i]["type"]  = @mysql_field_type  ($id, $i);
        $res[$i]["len"]   = @mysql_field_len   ($id, $i);
        $res[$i]["flags"] = @mysql_field_flags ($id, $i);
      }
    } else { // full
      $res["num_fields"]= $count;

      for ($i=0; $i<$count; $i++) {
        $res[$i]["table"] = @mysql_field_table ($id, $i);
        $res[$i]["name"]  = @mysql_field_name  ($id, $i);
        $res[$i]["type"]  = @mysql_field_type  ($id, $i);
        $res[$i]["len"]   = @mysql_field_len   ($id, $i);
        $res[$i]["flags"] = @mysql_field_flags ($id, $i);
        $res["meta"][$res[$i]["name"]] = $i;
      }
    }

    // free the result only if we were called on a table
    if ($table) @mysql_free_result($id);
    return $res;
  }

  /* private: error handling */
  function halt($msg) {
    $this->Error = @mysql_error($this->Link_ID);
    $this->Errno = @mysql_errno($this->Link_ID);
    if ($this->Halt_On_Error == "no")
      return;

    $this->haltmsg($msg);

    if ($this->Halt_On_Error != "report")
      die("Session halted. <br />".$msg);
  }

  function haltmsg($msg) {
   // printf("</td></tr></table><b>Database error:</b> %s<br>\n", $msg);
    //printf("<b>MySQL Error</b>: %s (%s)<br>\n", $this->Errno, $this->Error);
	printf("page error  <br />".$msg."<br />");
  }

//------------------------记录SQL LOG-----------------------------
function  log_result($word,$log_url) 
{
	//---获取客户段IP----------------
	
if(getenv('HTTP_CLIENT_IP')) { 
$onlineip = getenv('HTTP_CLIENT_IP');
} elseif(getenv('HTTP_X_FORWARDED_FOR')) { 
$onlineip = getenv('HTTP_X_FORWARDED_FOR');
} elseif(getenv('REMOTE_ADDR')) { 
$onlineip = getenv('REMOTE_ADDR');
} else { 
$onlineip = $HTTP_SERVER_VARS['REMOTE_ADDR'];
}
//echo $onlineip;
 
$admin_id=$_COOKIE[$this->Log_Cookie_User];
	//----结束------------------------
$message = date("Ymd H:i ").":IP:".$onlineip." 操作员ID:".$admin_id."\n\rURL:".$_SERVER['HTTP_REFERER']."\n\rSQL:".$word;

$message=base64_decode("DQo=").$message;
$x_dir=dirname(__FILE__).$log_url;//."/".date("Ymd");
if(file_exists($x_dir))
{
}
else
{
  mkdir($x_dir,0777);
	@chmod($x_dir,0777); 
}
$the_log_file=$x_dir."/".date("Ymd").".log";

if ( !($fp = @fopen($the_log_file, "a+")))
{
	echo "\n\rWarning: Cannot open  file \"".$the_log_file."\"\n";
 return FALSE;;
}
flock($fp, LOCK_EX);
fputs($fp, $message);
fclose($fp);
}
//----------------------------------------------------------------

  function table_names() {
    $this->query("SHOW TABLES");
    $i=0;
    while ($info=mysql_fetch_row($this->Query_ID))
     {
      $return[$i]["table_name"]= $info[0];
      $return[$i]["tablespace_name"]=$this->Database;
      $return[$i]["database"]=$this->Database;
      $i++;
     }
   return $return;
  }
  function queryArray($sql){
  	$this->query($sql);
  	$result=array();
  	while($this->next_record()){
	$res=(array)$this;
  		array_push($result,$res['Record']);
  	}
  	return $result;
  }

	function getNameByCode($item_type,$item_code){
		$name="";
		$name_sql="select ".$item_type."_name from goods_$item_type where ".$item_type."_code='".htmlspecialchars($item_code)."'";
		$name_array=$this->queryArray($name_sql);
		if(!empty($name_array)){
			$name=$name_array[0][$item_type."_name"];
		}
		return $name;
	}
  
  
    function check_table_exist($table_name)
  {
  	
  	if($table_name=="")
  	  return "-1";
  	$bm_str="show tables ";
    $bm_res=$rs=$this->query($bm_str);
    $bm=array();
    while($bmtmp=mysql_fetch_row($bm_res))
    {
    	 $bm[]=$bmtmp[0];
    } 
   
     if(in_array($table_name,$bm))
     
     	return 1;
     
     else
     
     	return 00;
     
  }
  
  }

?>